package com.hay.pay.support.ali;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.request.*;
import com.alipay.api.response.AlipayTradeCreateResponse;
import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.hay.pay.enums.PayChannelEnum;
import com.hay.pay.enums.RefundStatusEnum;
import com.hay.pay.exception.PayException;
import com.hay.pay.model.PayConfig;
import com.hay.pay.model.param.*;
import com.hay.pay.model.result.*;
import com.hay.pay.support.PayContext;
import com.hay.pay.support.PayPlugin;
import com.hay.pay.support.ali.enums.AliRefundStatusEnum;
import com.hay.pay.support.ali.enums.AliTradeStatusEnum;
import com.hay.pay.support.ali.param.AliJsApiPlaceOrderParam;
import com.hay.pay.support.ali.param.AlipayPayNoticeParam;
import com.hay.pay.support.ali.param.AlipayRefundNoticeParam;
import com.hay.pay.util.CurrencyUtil;
import com.hay.pay.util.DateUtil;
import com.hay.pay.util.WebUtil;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

/**
 * // toDO 该插件没有使用过
 * 阿里JSAPI支付插件
 * <a href="https://opendocs.alipay.com/mini/053llc?pathHash=d81d3a2a"/>详见</a>
 * @author LiMouRen
 * @date 2024/1/29
 */
@Slf4j
public class AliJsapiPayPlugin implements PayPlugin {

    public final static PayChannelEnum PAY_CHANNEL = PayChannelEnum.ALI_JSAPI;

    /**
     * 下单
     * <a href="https://opendocs.alipay.com/mini/05x9kv?scene=de4d6a1e0c6e423b9eefa7c3a6dcb7a5&pathHash=779dc517"/>详见</a>
     * @param placeOrderParam 下单参数
     * @return
     */
    @Override
    public PlaceOrderResult placeOrder(PlaceOrderParam placeOrderParam) {
        AliPayConfig payConfig = getAliPayConfig(placeOrderParam.getMerchantCode());
        // 构建请求
        AliPayRequest payRequest = AliPayRequest.init()
                .aliPayConfig(payConfig)
                .build();
        // 构建请求参数
        AliJsApiPlaceOrderParam params = placeOrderParam.getSceneParams(AliJsApiPlaceOrderParam.class);
        String notifyUrl = params.getNotifyUrl();
        AlipayTradeCreateRequest request = new AlipayTradeCreateRequest();
        JSONObject bizContent = convertBizContent(params);
        request.setBizContent(bizContent.toString());
        request.setNotifyUrl(notifyUrl);
        // 调用接口
        AlipayTradeCreateResponse response = payRequest.execute(request);
        PlaceOrderResult result = new PlaceOrderResult();
        result.setResult(response.getBody());
        result.setThirdTransactionCode(response.getTradeNo());
        result.setOrderCode(params.getOutTradeNo());
        result.setNotifyUrl(notifyUrl);
        return result;
    }

    /**
     * 关闭订单
     * @param closeOrderParam 取消订单参数
     */
    @Override
    public void closeOrder(CloseOrderParam closeOrderParam) {
        AliPayConfig payConfig = getAliPayConfig(closeOrderParam.getMerchantCode());
        // 构建请求
        AliPayRequest payRequest = AliPayRequest.init()
                .aliPayConfig(payConfig)
                .build();
        AlipayTradeCancelRequest request = AliPayApi.tradeCancelRequest(closeOrderParam.getOrderCode());
        payRequest.execute(request);
    }

    /**
     * <a href="https://opendocs.alipay.com/mini/05xsky?scene=common&pathHash=354e8be3"/>详见</a>
     * @param searchOrderParam 查询订单参数
     * @return
     */
    @Override
    public PayNotifyResult searchPlaceOrder(SearchPlaceOrderParam searchOrderParam) {
        AliPayConfig payConfig = getAliPayConfig(searchOrderParam.getMerchantCode());
        // 构建请求
        AliPayRequest payRequest = AliPayRequest.init()
                .aliPayConfig(payConfig)
                .build();
        // 调用接口
        AlipayTradeQueryRequest request = AliPayApi.tradeQueryRequest(searchOrderParam.getOrderCode());
        AlipayTradeQueryResponse response = payRequest.execute(request);
        return convertSearchPlaceOrderResult(response);
    }

    /**
     * 唤起收银台,调用下单接口后,拿到交易号,前端皆可唤起收银台,无需调用此方法
     * <a href="https://opendocs.alipay.com/mini/05xhsr?pathHash=d4709298"/>详见</a>
     * @param arouseCashierParam 参数
     * @return
     */
    @Override
    public JSONObject arouseCashier(ArouseCashierParam arouseCashierParam) {
        return JSONObject.parseObject(JSONObject.toJSONString(arouseCashierParam));
    }

    @Override
    public RefundOrderResult refundOrder(RefundOrderParam refundOrderParam) {
        AliPayConfig payConfig = getAliPayConfig(refundOrderParam.getMerchantCode());
        // 构建请求
        AliPayRequest payRequest = AliPayRequest.init()
                .aliPayConfig(payConfig)
                .build();
        AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
        String notifyUrl = refundOrderParam.getNotifyUrl();
        request.setNotifyUrl(notifyUrl);
        JSONObject bizContent = new JSONObject();
        bizContent.put("trade_no", refundOrderParam.getThirdTransactionCode());
        bizContent.put("out_trade_no", refundOrderParam.getOrderCode());
        bizContent.put("refund_amount", CurrencyUtil.fenToYuan(refundOrderParam.getRefundAmount()));
        request.setBizContent(bizContent.toString());
        AlipayTradeRefundResponse response = payRequest.execute(request);
        return convertRefundOrderResult(response);
    }

    @Override
    public RefundNotifyResult searchRefundOrder(SearchRefundOrderParam refundOrderParam) {
        AliPayConfig payConfig = getAliPayConfig(refundOrderParam.getMerchantCode());
        // 构建请求
        AliPayRequest payRequest = AliPayRequest.init()
                .aliPayConfig(payConfig)
                .build();
        AlipayTradeFastpayRefundQueryRequest request = AliPayApi.tradeRefundQueryRequest(null, refundOrderParam.getOrderCode());
        AlipayTradeFastpayRefundQueryResponse response = payRequest.execute(request);
        return convertSearchRefundOrderResult(response);
    }

    /**
     * 处理支付回调
     * <a href="https://opendocs.alipay.com/support/01raw4?pathHash=fb66d8bb">详见</a>
     * @param request 请求
     * @param response 响应
     * @param merchantCode 商户号
     * @return
     */
    @Override
    public HandlePayNotifyResult handlePayNotify(HttpServletRequest request, HttpServletResponse response, String merchantCode) {
        HandlePayNotifyResult handleResult = new HandlePayNotifyResult();
        boolean notifySuccess = true;
        Map<String, String> pathParam = null;
        try {
            AliPayConfig payConfig = getAliPayConfig(merchantCode);
            pathParam = WebUtil.getRequestPathParam(request);
            Boolean verify = AliPaySign.verify(pathParam, payConfig.getAlipayPublicKey());
            if (!verify){
                throw new PayException("非法参数");
            }
            AlipayPayNoticeParam param = JSONObject.parseObject(JSONObject.toJSONString(pathParam), AlipayPayNoticeParam.class);
            PayNotifyResult result = covertPayNotifyResult(param);
            handleResult.setResult(result);
            payNotify(result);
        }catch (Exception e){
            String errorMessage =  e.getMessage();
            handleResult.setErrorMessage(errorMessage);
            log.error("handlePayNotify merchantCode: {}, pathParam:{}, error: {}",merchantCode,pathParam,errorMessage,e);
            notifySuccess = false;
        }
        AliPayRequest.noticeResponse(response,notifySuccess);
        handleResult.setNotifySuccess(notifySuccess);
        return handleResult;
    }

    /**
     *  // todo 没有接触过退款
     * 处理退款回调
     * @param request 请求
     * @param response 响应
     * @param merchantCode 配置编号
     */
    @Override
    public HandleRefundNotifyResult handleRefundNotify(HttpServletRequest request, HttpServletResponse response, String merchantCode) {
        HandleRefundNotifyResult handleResult = new HandleRefundNotifyResult();
        boolean notifySuccess = true;
        Map<String, String> pathParam = null;
        try {
            AliPayConfig payConfig = getAliPayConfig(merchantCode);
            pathParam = WebUtil.getRequestPathParam(request);
            Boolean verify = AliPaySign.verify(pathParam, payConfig.getAlipayPublicKey());
            if (!verify){
                throw new PayException("非法参数");
            }
            AlipayRefundNoticeParam param = JSONObject.parseObject(JSONObject.toJSONString(pathParam), AlipayRefundNoticeParam.class);
            RefundNotifyResult result = covertRefundNotifyResult(param);
            handleResult.setResult(result);
            refundNotify(result);
        }catch (Exception e){
            String errorMessage =  e.getMessage();
            log.error("handleRefundNotify configCode: {}, pathParam:{}, error: {}",merchantCode,pathParam,errorMessage,e);
            notifySuccess = false;
            handleResult.setErrorMessage(errorMessage);
        }
        AliPayRequest.noticeResponse(response,notifySuccess);
        handleResult.setNotifySuccess(notifySuccess);
        return handleResult;
    }

    private AliPayConfig getAliPayConfig(String merchantCode){
        PayConfig payConfig = PayContext.getPayConfig(PAY_CHANNEL.name(), merchantCode);
        return payConfig.getSceneParams(AliPayConfig.class);
    }


    private JSONObject convertBizContent(AliJsApiPlaceOrderParam params) {
        JSONObject bizContent = new JSONObject();
        bizContent.put("out_trade_no", params.getOutTradeNo());
        bizContent.put("total_amount", CurrencyUtil.fenToYuan(params.getTotalAmount()));
        bizContent.put("subject", params.getSubject());
        bizContent.put("product_code",params.getProductCode());
        bizContent.put("op_app_id",params.getOpAppId());
        bizContent.put("buyer_id",params.getBuyerId());
        bizContent.put("buyer_open_id",params.getBuyerOpenId());
        bizContent.put("timeout_expire", DateUtil.format(params.getTimeExpire(),"yyyy-MM-dd HH:mm:ss"));
        return bizContent;
    }

    private PayNotifyResult convertSearchPlaceOrderResult(AlipayTradeQueryResponse response) {
        PayNotifyResult result = new PayNotifyResult();
        result.setResult(response.getBody());
        result.setOrderCode(response.getOutTradeNo());
        result.setThirdTransactionCode(response.getTradeNo());
        result.setPayAmount(CurrencyUtil.yuanToFen(response.getTotalAmount()));
        result.setThirdPayerCode(response.getBuyerUserId());
        String tradeStatus = response.getTradeStatus();
        result.setThirdTransactionStatus(tradeStatus);
        if (AliTradeStatusEnum.TRADE_SUCCESS.name().equals(tradeStatus) || AliTradeStatusEnum.TRADE_FINISHED.name().equals(tradeStatus)){
            result.setSuccess(true);
            result.setPaySuccessTime(response.getSendPayDate());
        } else {
            result.setSuccess(false);
        }
        return result;
    }

    private RefundOrderResult convertRefundOrderResult(AlipayTradeRefundResponse response) {
        RefundOrderResult result = new RefundOrderResult();
        result.setResult(response.getBody());
        return result;
    }

    private RefundNotifyResult convertSearchRefundOrderResult(AlipayTradeFastpayRefundQueryResponse response) {
        RefundNotifyResult result = new RefundNotifyResult();
        result.setResult(response.getBody());
        result.setOrderCode(response.getOutTradeNo());
        result.setRefundAmount(CurrencyUtil.yuanToFen(response.getRefundAmount()));
        result.setThirdRefundCode(response.getOutRequestNo());
        result.setThirdTransactionCode(response.getTradeNo());
        String refundStatus = response.getRefundStatus();
        result.setThirdRefundStatus(refundStatus);
        if (AliRefundStatusEnum.REFUND_SUCCESS.name().equals(refundStatus)){
           result.setRefundStatusEnum(RefundStatusEnum.SUCCESS);
           result.setRefundSuccessTime(response.getGmtRefundPay());
        }else {
            result.setRefundStatusEnum(RefundStatusEnum.REFUNDING);
        }
        return result;
    }

    private PayNotifyResult covertPayNotifyResult(AlipayPayNoticeParam param) {
        PayNotifyResult result = new PayNotifyResult();
        result.setResult(param.toString());
        result.setOrderCode(param.getOut_trade_no());
        if (AliTradeStatusEnum.TRADE_SUCCESS.name().equals(param.getTrade_status())){
            result.setSuccess(true);
        }else {
            result.setSuccess(false);
        }
        return result;
    }

    private RefundNotifyResult covertRefundNotifyResult(AlipayRefundNoticeParam param) {
        return null;
    }

}
